package com.zishuimuyu.trans.impl;


import com.alibaba.fastjson.JSONArray;
import com.zishuimuyu.http.HttpParams;
import com.zishuimuyu.http.HttpPostParams;
import com.zishuimuyu.trans.AbstractOnlineTranslator;
import com.zishuimuyu.trans.LANG;
import com.zishuimuyu.trans.annotation.TranslatorComponent;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

/**
 * 使用 @TranslatorComponent 注解标记该类为翻译组件，其唯一标识符为 "google"
 */
@TranslatorComponent(id = "google")
/**
 * 最终的谷歌翻译器类，继承自 AbstractOnlineTranslator 类。
 * 该类实现了使用谷歌翻译 API 进行翻译的功能。
 */
final public class GoogleTranslator extends AbstractOnlineTranslator {
    /**
     * 静态的 JavaScript 脚本引擎，用于执行 JavaScript 代码以生成翻译所需的 tk 参数。
     * 建议将该字段声明为 final，因为其初始化后不会再改变。
     */
    private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");

    /**
     * 构造函数，用于初始化语言映射。
     * 此方法将内部语言枚举 LANG 映射到谷歌翻译 API 所支持的语言代码。
     */
    public GoogleTranslator() {
        // 将英文 LANG.EN 映射到谷歌翻译 API 支持的 "en" 代码
        langMap.put(LANG.EN, "en");
        // 将中文 LANG.ZH 映射到谷歌翻译 API 支持的 "zh-CN" 代码
        langMap.put(LANG.ZH, "zh-CN");
        // 将俄语 LANG.RU 映射到谷歌翻译 API 支持的 "ru" 代码
        langMap.put(LANG.RU, "ru");
    }

    /**
     * 重写父类的 getResponse 方法，用于向谷歌翻译 API 发送请求并获取响应。
     *
     * @param from  源语言，使用 LANG 枚举表示
     * @param targ  目标语言，使用 LANG 枚举表示
     * @param query 需要翻译的文本
     * @return 谷歌翻译 API 返回的响应字符串
     * @throws Exception 如果在发送请求过程中发生异常
     */
    @Override
    protected String getResponse(LANG from, LANG targ, String query) throws Exception {
        // 声明 HttpParams 对象，初始化为 null 是多余的，可直接初始化
        HttpParams params;
        // 创建一个 HttpPostParams 对象，用于构建 POST 请求的参数
        params = new HttpPostParams();        //统一采用post，若字符长度小于999用get也可以的
        // 调用 tk 方法生成翻译所需的 tk 参数
        String tk = tk(query);

        // 向请求参数中添加各种必要的参数
        params.put("client", "t")
                .put("sl", langMap.get(from))
                .put("tl", langMap.get(targ))
                .put("hl", "zh-CN")
                .put("dt", "at")
                .put("dt", "bd")
                .put("dt", "ex")
                .put("dt", "ld")
                .put("dt", "md")
                .put("dt", "qca")
                .put("dt", "rw")
                .put("dt", "rm")
                .put("dt", "ss")
                .put("dt", "t")

                .put("ie", "UTF-8")
                .put("oe", "UTF-8")
                .put("source", "btn")
                .put("srcrom", "1")
                .put("ssel", "0")
                .put("tsel", "0")
                .put("kc", "11")
                .put("tk", tk)
                .put("q", query);

        // 发送请求并将响应内容作为字符串返回
        return params.send2String("http://translate.google.cn/translate_a/single");
    }

    /**
     * 重写父类的 parseString 方法，用于解析谷歌翻译 API 返回的 JSON 响应字符串。
     *
     * @param jsonString 谷歌翻译 API 返回的 JSON 响应字符串
     * @return 解析后的翻译结果字符串
     */
    @Override
    protected String parseString(String jsonString) {
        // 将 JSON 字符串解析为 JSONArray 对象
        JSONArray jsonArray = JSONArray.parseArray(jsonString);
        // 从 JSONArray 中获取翻译结果数组
        JSONArray segments = jsonArray.getJSONArray(0);
        // 创建一个 StringBuilder 对象，用于拼接翻译结果
        StringBuilder result = new StringBuilder();

        // 遍历翻译结果数组
        for (int i = 0; i < segments.size(); i++) {
            // 将当前结果的翻译文本添加到 StringBuilder 中
            result.append(segments.getJSONArray(i).getString(0));
        }

        // 将 StringBuilder 转换为字符串并返回
        return new String(result);
    }

    /**
     * 生成谷歌翻译所需的 tk 参数的方法。
     *
     * @param val 需要翻译的文本
     * @return 生成的 tk 参数
     * @throws Exception 如果在执行 JavaScript 代码时发生异常
     */
    private String tk(String val) throws Exception {
        // 定义 JavaScript 代码，用于生成 tk 参数
        String script = "function tk(a) {"
                + "var TKK = ((function() {var a = 561666268;var b = 1526272306;return 406398 + '.' + (a + b); })());\n"
                + "function b(a, b) { for (var d = 0; d < b.length - 2; d += 3) { var c = b.charAt(d + 2), c = 'a' <= c ? c.charCodeAt(0) - 87 : Number(c), c = '+' == b.charAt(d + 1) ? a >>> c : a << c; a = '+' == b.charAt(d) ? a + c & 4294967295 : a ^ c } return a }\n"
                + "for (var e = TKK.split('.'), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {"
                + "var c = a.charCodeAt(f);"
                + "128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ? (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240, g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)"
                + "}"
                + "a = h;"
                + "for (d = 0; d < g.length; d++) a += g[d], a = b(a, '+-a^+6');"
                + "a = b(a, '+-3^+b+-f');"
                + "a ^= Number(e[1]) || 0;"
                + "0 > a && (a = (a & 2147483647) + 2147483648);"
                + "a %= 1E6;"
                + "return a.toString() + '.' + (a ^ h)\n"
                + "}";

        // 在 JavaScript 引擎中执行定义的脚本
        engine.eval(script);
        // 将脚本引擎转换为可调用对象
        Invocable inv = (Invocable) engine;
        // 调用 JavaScript 函数生成 tk 参数并返回
        return (String) inv.invokeFunction("tk", val);
    }
}
